{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 面向对象的高级编程"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1 使用\\__slots\\__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1. 给实例绑定任何属性和方法体现了动态语言的灵活性\n",
    "2. python中所有的函数、变量都可以视为对象，所以类的属性也可以当做函数来使用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.实例绑定属性的结果: {'name': 'Michael', 'my_print': <function myprint at 0x000001D6436E2D90>}\n",
      "2.绑定方法后的结果:\n",
      "  新增了属性age: 25\n",
      "  当前所有属性: {'name': 'Michael', 'my_print': <function myprint at 0x000001D6436E2D90>, 'set_age': <bound method set_age of <__main__.Student object at 0x000001D6437031D0>>, 'age': 25}\n"
     ]
    }
   ],
   "source": [
    "# 1. 为实例绑定新的属性及方法\n",
    "class Student(object):\n",
    "    pass\n",
    "s = Student()\n",
    "s.name = 'Michael'         # 给实例绑定属性\n",
    "def myprint():\n",
    "    print('hello world')\n",
    "s.my_print = myprint       # 这里是把实例的属性赋值为函数对象了，它仍属于实例的属性，而不是方法\n",
    "print('1.实例绑定属性的结果:', s.__dict__)\n",
    "\n",
    "def set_age(self, age):\n",
    "    self.age = age\n",
    "from types import MethodType\n",
    "s.set_age = MethodType(set_age, s)   # 给实例绑定一个方法，其实还是属性，用__dict__仍能看到\n",
    "s.set_age(25)\n",
    "print('2.绑定方法后的结果:')\n",
    "print('  新增了属性age:', s.age)\n",
    "print('  当前所有属性:', s.__dict__)  # __dict__只输出属性，不包括类中定义的方法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "给特定实例绑定的属性及方法，对该类创建的其他实例是无效的\n",
    "- 解决方法：给类绑定属性及方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.类绑定方法的结果: 97 99\n",
      "hello world!\n"
     ]
    }
   ],
   "source": [
    "# 2. 给类绑定属性及方法\n",
    "class Student(object):       # 新建的类\n",
    "    pass\n",
    "def set_score(self, score):  # 需动态增加的方法\n",
    "    self.score = score\n",
    "def myprint(self):           # 注意与实例绑定属性中的函数对比，需要多加一个self\n",
    "    print('hello world!')\n",
    "Student.set_score = set_score\n",
    "Student.myprint = myprint\n",
    "s1 = Student()\n",
    "s2 = Student()\n",
    "s1.set_score(97)       # 类绑定方法后，所以实例都可以调用\n",
    "s2.set_score(99)\n",
    "print('1.类绑定方法的结果:', s1.score, s2.score)\n",
    "s1.myprint()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "小结:\n",
    "- 在实例和类绑定方法时，都可以使用`XXX.attribute_name = func_name`，但在类中添加的方法函数第一个参数必须为self(其实换个名称也行，但为了与类定义对应，使用self)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用\\__slots\\__限制实例的属性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.类绑定属性及方法不受__slots__限制: 99\n"
     ]
    }
   ],
   "source": [
    "# 3. 使用__slots__限制属性名称\n",
    "class Student(object):\n",
    "    __slots__ = ('name', 'age')    # 用tuple定义允许绑定的属性名称\n",
    "s = Student()\n",
    "s.name = 'Michael'      # 为实例绑定新属性\n",
    "s.age = 25\n",
    "# s.score = 100         # score没有再__slots__中，所以会报错\n",
    "\n",
    "def set_score(self, score):  # 需动态增加的方法\n",
    "    self.score = score\n",
    "Student.set_score = set_score       # 给类绑定方法及属性，不受__slots__限制，但实例不行\n",
    "# Student.set_score(Student, 99)    # 给自己绑定属性\n",
    "Student.score = 99                  # 只读，实例中不可以修改\n",
    "# Student.set_score(3)\n",
    "s = Student()\n",
    "print('1.类绑定属性及方法不受__slots__限制:', s.score)\n",
    "# s = Student()\n",
    "# s.score"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# __slots__定义的属性对当前实例起作用，对继承的子类不起作用，除非子类中也加入__slots__\n",
    "class GraduateStudent(Student):\n",
    "    __slots__ = ('score')        # 加入该句后，才能限制类属性名称，同时可使用父类的__slots__\n",
    "    pass\n",
    "g = GraduateStudent()\n",
    "g.score = 9999\n",
    "g.name = 'd'\n",
    "# g.ss = 'f'         # 该句报错，原因父类及子类的__slots__中都没有ss属性"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "小结:\n",
    "- 类中绑定属性及方法不受\\__slots\\__的限制，只有实例才受限制\n",
    "- 子类中同样使用\\__slots\\__，才能起到限制属性作用，限制范围:父类+子类的范围"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2 使用@proerty"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.1回顾装饰器的用法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "---------------------------------------------------\n",
      "1.这是修饰器添加的部分: Current date: 2018-08-12 13:47:00\n",
      "2.这是函数本身的输出: Hello world! Hello python!\n"
     ]
    }
   ],
   "source": [
    "# 1. 使用装饰器为函数动态的添加功能，例如函数执行前，打印日志\n",
    "import functools   \n",
    "import datetime\n",
    "\n",
    "def log(text):             # text参数表示准备输出的信息\n",
    "    def decorator(func):   # func需要修饰的函数，这里调用hello即可\n",
    "        @functools.wraps(func)       # 保证修饰后的函数名称保持不变\n",
    "        def wrapper(*args, **kw):   # 可变参数保证待修饰函数正常运行，该部分是添加额外功能的地方\n",
    "            now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')  # 获取当前日期\n",
    "            print('{0} Current date: {1}'.format(text, now))             # 添加的功能部分\n",
    "            return func(*args, **kw)      # 执行func函数并返回\n",
    "        return wrapper                    # 返回装饰后的函数\n",
    "    return decorator                      # 返回装饰器\n",
    "\n",
    "@log('1.这是修饰器添加的部分:')             # 在函数定义的前面加\n",
    "def hello():     # 为该函数添加装饰器，增加输出当前日期的功能\n",
    "    print('2.这是函数本身的输出: Hello world! Hello python!')\n",
    "print('---------------------------------------------------')\n",
    "hello()          # 调用装饰后的函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.2 @property装饰器将方法变成属性调用\n",
    "即使用属性调用的方式，同时可检查参数的合理性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.使用属性修饰器的结果: 99\n"
     ]
    }
   ],
   "source": [
    "# 2. 属性修饰器方便又好用\n",
    "class Student(object):\n",
    "    @property\n",
    "    def score(self):         # 1\n",
    "        return self._score   # _score名称可以改变(属性名之外名称)，但约定俗成，属性名前加单个下划线\n",
    "    @score.setter            # 2\n",
    "    def score(self, value):  # 3 ，其中1,2,3处的score名称一定要统一\n",
    "        if not isinstance(value, int):\n",
    "            raise ValueError('Score must be an integer!')\n",
    "        if value < 0 or value > 100:\n",
    "            raise ValueError('Score must between 0 ~ 100!')\n",
    "        self._score = value\n",
    "s = Student()\n",
    "s.score = 99\n",
    "print('1.使用属性修饰器的结果:', s.score)\n",
    "# s.score = 1000     # 检查属性的合理性，不合理会报错"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "小结:\n",
    "- @property 属性装饰器把getter方法变为属性\n",
    "- @attribute_name.setter 把setter方法变为属性赋值，如果没有该步骤，则属性变为只读"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "属性装饰器练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.可读写属性: 1024 768\n",
      "2.只读属性resolution: 786432\n"
     ]
    }
   ],
   "source": [
    "# @property给一个Screen对象加上width和height属性，以及一个只读属性resolution\n",
    "class Screen(object):\n",
    "#     def __init__(self):      # 初始化省略也可以\n",
    "#         self._width = 0\n",
    "#         self._height = 0\n",
    "#         self._resolution = 0\n",
    "    @property                # 添加width属性\n",
    "    def width(self):\n",
    "        return self._width\n",
    "    @width.setter            # 使width属性可修改\n",
    "    def width(self, value):\n",
    "        if not isinstance(value, int):\n",
    "            raise ValueError('Value must be integer!')\n",
    "        else:\n",
    "            self._width = value\n",
    "        \n",
    "    @property                # 添加height属性\n",
    "    def height(self):\n",
    "        return self._height\n",
    "    @height.setter\n",
    "    def height(self, value):\n",
    "        if not isinstance(value, int):\n",
    "            raise ValueError('Value must be integer!')\n",
    "        else:\n",
    "            self._height = value\n",
    "    \n",
    "    @property\n",
    "    def resolution(self):    # 该属性只读\n",
    "        return self._width * self._height\n",
    "\n",
    "s = Screen()\n",
    "s.width = 1024\n",
    "s.height = 768\n",
    "print('1.可读写属性:', s.width, s.height)\n",
    "print('2.只读属性resolution:', s.resolution)   "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3 多重继承"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "一个子类可以继承多个父类，举例Dog、Bat、Parrot和Ostrich"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "分类结果: \n",
      "                Animal\n",
      "        __________|__________\n",
      "       |                     |\n",
      "    Runnable              Flyable\n",
      "  _____|_____           _____|_____ \n",
      " |           |         |           |\n",
      "Dog       Ostrich   Parrot        Bat\n",
      "\n"
     ]
    }
   ],
   "source": [
    "class_str=\"\"\"\n",
    "                Animal\n",
    "        __________|__________\n",
    "       |                     |\n",
    "    Runnable              Flyable\n",
    "  _____|_____           _____|_____ \n",
    " |           |         |           |\n",
    "Dog       Ostrich   Parrot        Bat\n",
    "\"\"\"\n",
    "print('分类结果:', class_str)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在多重继承中，每种子类都有一个主线继承的父类，其他继承的父类是额外\"混入\"的，这种方式成为MixIn模式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 动物分类\n",
    "class Animal(object):   # 最基本的动物类\n",
    "    pass\n",
    "\n",
    "# 子类要继承的主线类\n",
    "class Mammal(Animal):   # 从Animal类继承来的主要类\n",
    "    pass\n",
    "class Bird(Animal):\n",
    "    pass\n",
    "\n",
    "# 各种动物\n",
    "class Dog(Mammal):     # 基本的继承关系\n",
    "    pass\n",
    "class Bat(Mammal):\n",
    "    pass\n",
    "class Parrot(Bird):\n",
    "    pass\n",
    "class Ostrich(Bird):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 需要添加额外的父类，进行多重继承\n",
    "class RunnableMixIn(object):    # 定义额外的父类\n",
    "    def run(self):\n",
    "        print('Running...')\n",
    "class FlyableMixIn(object):\n",
    "    def fly(self):\n",
    "        print('flying...')\n",
    "class Dog(Mammal, RunnableMixIn):  # 即继承了Mammal，同时继承了RunnableMixIn\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "多重继承练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.显示继承的顺序: (<class '__main__.D'>, <class '__main__.C1'>, <class '__main__.C2'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)\n",
      "2.调用父类方法:沿着继承的顺序，从左边起先出现具有该方法的父类，然后调用该方法:\n",
      "C1-foo\n",
      "A bar\n"
     ]
    }
   ],
   "source": [
    "# 使用DAG，有向无环图，c3算法来实现多重继承\n",
    "class A(object):          #定义两个基类\n",
    "    def foo(self):\n",
    "        print('A foo')\n",
    "    def bar(self):\n",
    "        print('A bar')\n",
    "class B(object):\n",
    "    def foo(self):\n",
    "        print('B foo')\n",
    "    def bar(self):\n",
    "        print('B bar')\n",
    "class C1(A, B):          # 对A和B进行了多重继承\n",
    "    def foo(self):\n",
    "        print('C1-foo')\n",
    "class C2(A, B):\n",
    "    def foo(self):\n",
    "        print('C2-foo')\n",
    "\n",
    "class D(C1, C2):         # D对C1和C2进行了多重继承\n",
    "    pass\n",
    "if __name__ == '__main__':\n",
    "    print('1.显示继承的顺序:', D.__mro__)     # 显示继承的顺序\n",
    "    d = D()\n",
    "    print('2.调用父类方法:沿着继承的顺序，从左边起先出现具有该方法的父类，然后调用该方法:')\n",
    "    d.foo()\n",
    "    d.bar()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "小结:\n",
    "- 多重继承组合多个MixIn的功能，即拥有所有已继承父类的属性及方法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4 定制类"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\\__XXX\\__变量相关，该类变量有特殊用途"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  1. \\__str\\__\n",
    "输出实例的信息"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.实例的信息: <__main__.Student object at 0x000001D643747940>\n",
      "2.重定义__str__()方法后的结果: Student object (name: David)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<__main__.Student at 0x1d643747978>"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 该字符串是记录实例信息的\n",
    "class Student(object):\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "s = Student('Mike')\n",
    "print('1.实例的信息:', s)   # 该信息与__str__字符串有关 \n",
    "class Student(object):\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "    def __str__(self):\n",
    "        return 'Student object (name: %s)' % self.name\n",
    "s1 = Student('David')\n",
    "print('2.重定义__str__()方法后的结果:', s1)\n",
    "s1   # 但直接输出该实例信息，使用的是改变前的结果，这样调用的是__repr__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2.重定义__str__()方法后的结果: Student object (name: David)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "Student object (name: David)"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 为了使两种输出的结果都改变，可以将__repr__重写，但该方法与__str__一致，所以直接将__str__函数对象\n",
    "# 指向__repr__即可\n",
    "class Student(object):\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "    def __str__(self):    # 重定义而改变实例输出的信息\n",
    "         return 'Student object (name: %s)' % self.name\n",
    "    __repr__ =  __str__   # 将__str__函数对象指向__repr__，相当于重定义了相同的该函数\n",
    "s = Student('David')\n",
    "print('2.重定义__str__()方法后的结果:', s)\n",
    "s   # 但直接输出该实例信息，使用的是改变前的结果，这样调用的是__repr__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. \\__iter\\__\n",
    "与\\__next\\__方法一起实现对象的可迭代，可以使用for循环遍历，如list或tuple"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 "
     ]
    }
   ],
   "source": [
    "# 实现斐波那契数列\n",
    "class Fib(object):\n",
    "    def __init__(self):\n",
    "        self.a, self.b = 0, 1   # 初始化前两个数\n",
    "    def __iter__(self):\n",
    "        return self             # 实例本身是迭代对象，所以返回自己\n",
    "    def __next__(self):\n",
    "        self.a, self.b = self.b, self.a + self.b   # 计算下一个值\n",
    "        if self.a > 1000:\n",
    "            raise StopIteration('The loop is stopped.')   # 为什么输出不提示超出范围？？？？\n",
    "        return self.a\n",
    "fib = Fib()\n",
    "for i in fib:\n",
    "    print(i, end=' ')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. \\__getitem\\__\n",
    "获取指定下标的元素"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.获取指定下标的元素: 1 1 2 3 5 8 13 21 34 55 "
     ]
    }
   ],
   "source": [
    "# 1. 使用__getitem__可以访问指定下标元素\n",
    "class Fib(object):\n",
    "    def __getitem__(self, n):   # 该方法使实例可以使用f[index]的方式访问，但是只读的\n",
    "        self.a, self.b = 1, 1\n",
    "        for x in range(n):\n",
    "            self.a, self.b = self.b, self.a + self.b\n",
    "        return self.a\n",
    "#     def __setitem__(self, key, value):    # f[1] = 2333功能没有实现？？？？？？\n",
    "# #         self[key] = value\n",
    "# #         print(self._mylist)\n",
    "#         self._mylist[key] = value\n",
    "#         print('new key:', self[key], type(self[key]), key, value, self)\n",
    "\n",
    "f = Fib()\n",
    "print('1.获取指定下标的元素:', end=' ')\n",
    "for i in range(10):\n",
    "    print(f[i], end=' ')\n",
    "# print()\n",
    "# f[1] = 2333\n",
    "# print(f[2])\n",
    "# print('***')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.获取指定下标的元素: 1 1 2 3 5 8 13 21 34 55 \n",
      "2.切片的访问方法: [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]\n"
     ]
    }
   ],
   "source": [
    "# 2. 添加切片访问的功能\n",
    "class Fib(object):\n",
    "    def __getitem__(self, n):\n",
    "        if isinstance(n, int):       # 下标的访问形式\n",
    "            self.a, self.b = 1, 1\n",
    "            for x in range(n):\n",
    "                self.a, self.b = self.b, self.a + self.b\n",
    "            return self.a\n",
    "        if isinstance(n, slice):  # 切片的访问形式\n",
    "            start = n.start if n.start else 0   # 获取切片的起始和终止位置, f[:7]这种也可以正常访问\n",
    "            stop  = n.stop\n",
    "            self.a, self.b = 1, 1\n",
    "            self.L = []\n",
    "            for x in range(stop):               # 循环迭代创建序列\n",
    "                if x >= start:\n",
    "                    self.L.append(self.a)\n",
    "                self.a, self.b = self.b, self.a + self.b\n",
    "            return self.L        \n",
    "f = Fib()\n",
    "print('1.获取指定下标的元素:', end=' ')\n",
    "for i in range(10):\n",
    "    print(f[i], end=' ')\n",
    "print('\\n2.切片的访问方法:', f[:10])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. \\__getattr\\__\n",
    "可以动态返回一个属性，即还没有定义的属性，当调用不存在的属性时调用次方法来获取属性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.实例已有的属性: Mike\n",
      "2.实例动态输出没有定义的属性score: 100\n",
      "3.实例动态输出没有定义的属性age: 25\n",
      "4.既无定义也不在动态输出范围的属性则返回: None\n"
     ]
    }
   ],
   "source": [
    "# 动态返回属性\n",
    "class Student(object):\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "    def __getattr__(self, attr):    # 当输出name外的属性时，调用该方法\n",
    "        if attr == 'score':         # 如果输出的属性时score，则返回100，score，name外属性返回None\n",
    "            return 100              # 返回值\n",
    "        if attr == 'age':\n",
    "            return lambda : 25      # 返回函数\n",
    "#         raise AttributeError(\"Student object has no attribute %s\" % attr)  # 可以抛出异常\n",
    "s = Student('Mike')\n",
    "print('1.实例已有的属性:', s.name)\n",
    "print('2.实例动态输出没有定义的属性score:', s.score)\n",
    "print('3.实例动态输出没有定义的属性age:', s.age())        # 调用方式不同，多个括号\n",
    "print('4.既无定义也不在动态输出范围的属性则返回:', s.abc)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.动态链式调用的结果: /status/user/timeline/list\n"
     ]
    }
   ],
   "source": [
    "# 链式动态调用url\n",
    "class Chain(object):\n",
    "    def __init__(self, path=''):\n",
    "        self._path = path\n",
    "    def __getattr__(self, path):   # 动态调用\n",
    "        return Chain('%s/%s' % (self._path, path))\n",
    "    def __str__(self):\n",
    "        return self._path\n",
    "    __repr__ = __str__\n",
    "out = Chain().status.user.timeline.list     # 调用__str__方法输出类的信息\n",
    "print('1.动态链式调用的结果:', out)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. \\__call\\__\n",
    "含有该方法的类的实例可以直接当做函数调用，达到()调用的结果，相当于重载了括号运算符"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.实例本身作为方面调用: My name is Mike, my age is 25\n",
      "2.有__call__方法的类定义的实例可调用: True\n"
     ]
    }
   ],
   "source": [
    "# 直接调用实例，一般调用实例方式:instance.method(parameters)\n",
    "class Student(object):\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "    def __call__(self, age):    # 可以带参数\n",
    "        print('My name is %s, my age is %s' % (self.name, age))\n",
    "s = Student('Mike')\n",
    "print('1.实例本身作为方面调用:', end=' ')\n",
    "s(25)\n",
    "print('2.有__call__方法的类定义的实例可调用:', callable(s))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "动态调用练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.动态链式调用的结果:\n",
      "call __getattr__(status)\n",
      "call __getattr__(user)\n",
      "call __getattr__(timeline)\n",
      "call __getattr__(list)\n",
      "/status/user/timeline/list\n",
      "2.动态链式调用的结果:\n",
      "call __getattr__(users)\n",
      "call __call__(Mike)\n",
      "call __getattr__(repos)\n",
      "/users/Mike/repos\n"
     ]
    }
   ],
   "source": [
    "# 动态调用特性，类的所有属性和方法动态化处理\n",
    "class Chain(object):\n",
    "    def __init__(self, path=''):\n",
    "        self._path = path\n",
    "    def __getattr__(self, path):   # 动态属性调用\n",
    "        print('call __getattr__(%s)' % path)\n",
    "        return Chain('%s/%s' % (self._path, path))\n",
    "    def __str__(self):             # 输出实例的信息\n",
    "        return self._path\n",
    "    def __call__(self, param):\n",
    "        print('call __call__(%s)' % param)\n",
    "        return Chain('%s/%s' % (self._path, param))\n",
    "    __repr__ = __str__\n",
    "print('1.动态链式调用的结果:')\n",
    "out = Chain().status.user.timeline.list     # 调用__str__方法输出类的信息\n",
    "print(out)\n",
    "print('2.动态链式调用的结果:')\n",
    "out = Chain().users('Mike').repos           # 调用__str__方法输出类的信息\n",
    "print(out)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5 枚举类\n",
    "枚举类型属于同一个类，每个常量是类的唯一实例，方便管理相同类型的常量数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Jan => Month.Jan  1\n",
      "Feb => Month.Feb  2\n",
      "Mar => Month.Mar  3\n",
      "Apr => Month.Apr  4\n",
      "May => Month.May  5\n",
      "Jun => Month.Jun  6\n",
      "Jul => Month.Jul  7\n",
      "Aug => Month.Aug  8\n",
      "Sep => Month.Sep  9\n",
      "Oct => Month.Oct  10\n",
      "Nov => Month.Nov  11\n",
      "Dec => Month.Dec  12\n"
     ]
    }
   ],
   "source": [
    "# 保存月份\n",
    "from enum import Enum\n",
    "# 使用枚举类定义变量，定义Month类型的枚举类，value属性自动赋值，默认从1开始\n",
    "Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', \\\n",
    "                       'Sep', 'Oct', 'Nov', 'Dec'))\n",
    "for name, member in Month.__members__.items():\n",
    "    print(name, '=>', member, '', member.value)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Weekday.Mon 1\n",
      "Weekday.Tue\n",
      "sun => Weekday.sun  0\n",
      "Mon => Weekday.Mon  1\n",
      "Tue => Weekday.Tue  2\n",
      "Wed => Weekday.Wed  3\n",
      "Thu => Weekday.Thu  4\n",
      "Fri => Weekday.Fri  5\n",
      "Sat => Weekday.Sat  6\n"
     ]
    }
   ],
   "source": [
    "# Enum派生类更精确地控制枚举类型\n",
    "from enum import Enum, unique\n",
    "\n",
    "@unique              # unique装饰器保证数据无重复\n",
    "class Weekday(Enum):\n",
    "    sun = 0\n",
    "    Mon = 1\n",
    "    Tue = 2\n",
    "    Wed = 3\n",
    "    Thu = 4\n",
    "    Fri = 5\n",
    "    Sat = 6\n",
    "day1 = Weekday.Mon\n",
    "print(day1, day1.value)\n",
    "print(Weekday['Tue'])\n",
    "for name, member in Weekday.__members__.items():\n",
    "    print(name, '=>', member,'',member.value)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "枚举练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "My name is David, my gender is Gender.Male\n"
     ]
    }
   ],
   "source": [
    "# 把Student的gender属性改造为枚举类型，可以避免使用字符串\n",
    "from enum import Enum\n",
    "class Gender(Enum):     # 派生Gender枚举类\n",
    "    Male = 0\n",
    "    Female = 1\n",
    "class Student(object):\n",
    "    def __init__(self, name, gender):\n",
    "        self.name = name\n",
    "        self.gender = gender\n",
    "    def print_info(self):\n",
    "        print('My name is {}, my gender is {}'.format(self.name, self.gender))\n",
    "s = Student('David', Gender.Male)    # 没有使用字符串\n",
    "s.print_info()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6 元类"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "type()函数可以创建类\n",
    "1. class的名称\n",
    "2. 继承的父类集合，注意Python支持多重继承，如果只有一个父类，别忘了tuple的单元素写法\n",
    "3. class的方法名称与函数绑定，这里我们把函数fn绑定到方法名hello上"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello, world\n",
      "<class 'type'>\n",
      "<class '__main__.Hello'>\n"
     ]
    }
   ],
   "source": [
    "# type定义类\n",
    "def fn(self, name='world'):\n",
    "    print('Hello, %s' % name)\n",
    "Hello = type('Hello', (object,), dict(hello=fn))\n",
    "h = Hello()   # 实例化\n",
    "h.hello()     # 调用函数\n",
    "print(type(Hello))\n",
    "print(type(h))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**metaclass可以创建类**\n",
    "该部分不常用，所以直接略过"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
